package com.jdbc.jdbc;

import java.sql.*;
import java.util.Properties;
import java.util.Scanner;

/**
 * JDBC-2
 * Description:
 * 1. 详解java.sql api
 * 2. 模拟数据库查询操作，输入要查询的用户信息id、account，查询数据库t_user，反馈查询结果
 *
 * TODO：JDBC实现
 *    1. 注册驱动：通过反射方式```Class.forName("com.mysql.cj.jdbc.Driver")```注册```Driver```。
 *    2. 获取连接：通过重载方法```DriverManager.getConnection()```的不同传参方式创建连接```Connection```。
 *    3. 创建对象：通过```Connection```创建发送SQL的对象```Statement```。
 *    4. 执行SQL：通过```Statement```的```executeQuery```方法执行SQL并获取查询结果```ResultSet```。
 *    5. 结果解析：通过```ResultSet```的```next()```方法判断是否还有结果数据，通过```getObject()、getString()```等获取字段值。
 *    6. 关闭资源：关闭```ResultSet、Statement、Connection```。
 */
public class JdbcStatementLoginPart {

    public static void main(String[] args) throws ClassNotFoundException, SQLException {

        // 键盘输入要查询的用户信息id和account：1,root
        Scanner scanner = new Scanner(System.in);
        String id = scanner.nextLine();
        String account = scanner.nextLine();
        scanner.close();

        // 1.注册驱动
        /**
         * 方法一：
         *  1. 实现：DriverManager.registerDriver(new Driver());
         *  2. 问题：重复注册驱动，传参Driver类中的静态代码块同样调用了registerDriver()方法，类加载会触发静态代码块执行
         *  3. 扩展：
         *      类加载步骤： java文件 -> 编译 -> 【 class字节码文件 -->  类加载 --> jvm虚拟中  --> Class对象】
         *          1. 加载 【class文件转成对象加载到虚拟机中】
         *          2. 连接 【验证（检查类文件） -> 准备 (静态变量赋默认值) -> 解析 (触发静态代码块) 】
         *          3. 初始化【静态属性赋真实值】
         *
         *      以下7种方式会触发类加载：
         *          1. new关键字
         *          2. 调用静态属性
         *          3. 调用静态方法
         *          4. 接口 包含1.8 新特性 default关键字
         *          5. 反射 【Class.forName() 类名.class】
         *          6. 子类调用会触发父类的静态代码块
         *          7. 触发类的入口方法main
         *
         * 方法二：
         *  1. 实现：new Driver();
         *  2. 问题：依赖MySQL驱动的实现类Driver，不方便做动态变更，如果替换成其他数据库需要修改代码
         *
         * 方法三：
         *  1. 实现：Class.forName("com.mysql.cj.jdbc.Driver");
         *  2. 解决：通过反射方式，触发Driver类加载，触发静态代码执行
         *  3. 把字符串提取到外部配置文件，方便后续切换为其他数据库的驱动
         */
        // 方法一
//        DriverManager.registerDriver(new Driver());

        // 方法二
//        new Driver();

        // 方法三，通过反射方式，触发Driver类加载，触发静态代码执行，可以把字符串提取到外部配置文件
        Class.forName("com.mysql.cj.jdbc.Driver");

        // 2.获取连接
        /**
         * DriverManager.getConnection() 是重载方法，方便通过多种方式传递参数
         * 方法一：三个参数
         *  1. String URL：数据库地址
         *      jdbc:数据库管理软件名[mysql|oracle]://ip地址|主机名:端口号port/数据库名
         *      jdbc:mysql://192.168.**.**:3306/jdbc
         *      jdbc:mysql://localhost:3306/jdbc
         *  2. String user: 数据库用户名
         *  3. String password: 数据库用户密码
         *
         * 方法二：两个参数
         *  1. String URL
         *  2. java.util.Properties: 封装参数的Map容器,至少要包含user、password连接数据库的账号信息
         *
         * 方法三：一个参数
         *  1. String URL，可以通过?分割可选信息，以?key=value&key=value形式传递参数
         *      jdbc:mysql://localhost:3306/jdbc?user=root&password=root
         *      时区 & 编码 & SSL，驱动8版本以后，都是可选的默认值
         *      &serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=true
         */

        // 方法一：三个参数
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc", "root", "root");

        // 方法二：两个参数
//        Properties info = new Properties();
//        info.put("user", "root");
//        info.put("password", "root");
//        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc", info);

        // 方法三：一个参数
//        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc?user=root&password=root");

        // 3. 创建statement对象
        Statement statement = connection.createStatement();

        // 4. 执行SQL语句，并获取返回结果
        /**
         *  1. 动态SQL语句,需要字符串拼接，问题：当输入的 account 为 ' or '1' = '1  时，整条SQL语句为true，发生SQL注入
         *  2  执行SQL方法
         *      executeQuery(sql)  执行DQL语句，返回ResultSet结果集对象
         *      executeUpdate(sql) 执行非DQL语句，返回int响应行数
         */
        String sql = "select * from t_user where id = '"+id+"' and account = '"+account+"' ;";
        ResultSet resultSet = statement.executeQuery(sql);

        // 5. 结果集ResultSet解析
        /**
         * 1. 获取行数据 resultSet.next()
         * 通过next()方法移动游标cursor，获取行数据，默认指向第一行结果的上边
         * 返回结果为boolean类型，false: 没有数据，也不移动了，true:  有更多行，并且移动到下一行。
         * 推荐使用if或者while循环，嵌套next方法，循环和判断体内获取数据
         *
         * 2. 获取当前行的列数据 resultSet.get类型()
         * get类型(int columnIndex)     列的角标，从左到右，从1开始，数据库全是从1开始
         * get类型(String columnLabel)  列名获取，输入结果集的字段名，有别名用别名
         */
        while (resultSet.next()) {
            int id1 = resultSet.getInt("id");
            Object account1 = resultSet.getObject("account");
            String password = resultSet.getString(2);
            String nickname = resultSet.getString("nickname");
            System.out.println("user id="+ id1 + "::" + account1 + "::" + password + "::nickname=" +nickname);
        }

        // 7. 关闭资源
        resultSet.close();
        statement.close();
        connection.close();
    }
}
